package cn.outsourcing.supermarket.common.utils;

import cn.outsourcing.supermarket.common.constants.CommonConstants;
import cn.outsourcing.supermarket.common.constants.WechatConstants;
import com.alibaba.fastjson.JSONArray;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.core.authority.SimpleGrantedAuthority;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Jwt 工具类
 *
 * @author gzkemays
 * @since 2021/10/21 11:00
 */
@Getter
@Setter
public class JwtUtils {
  private String subject;
  private String secret;
  private String header;
  private String prefix;
  private String roleClams;
  private long expired;

  public static String getTokenByRequest(HttpServletRequest request) {
    return request.getHeader(CommonConstants.TOKEN_HEADER_KEY);
    //      Claims claims = JwtUtils.settings().build().checkJwt(token);
  }

  public static Object getTokenClaimByRequest(HttpServletRequest request, String key) {
    String token = request.getHeader(CommonConstants.TOKEN_HEADER_KEY);
    if (!StringUtils.isBlank(token)) {
      Claims claims = JwtUtils.settings().build().checkJwt(token);
      return claims.get(key);
    }
    if (key.equals(CommonConstants.TOKEN_USER_ID_KEY) || WechatConstants.OPEN_ID.equals(key)) {
      return 0;
    }
    return null;
  }

  private JwtUtils() {}

  private JwtUtils(JwtUtilBuilder builder) {
    subject = builder.subject;
    secret = builder.secret;
    header = builder.header;
    prefix = builder.prefix;
    roleClams = builder.roleClams;
    expired = builder.expired;
  }

  public static class JwtUtilBuilder {
    private String subject = "yeizjigroup-subject";
    private String secret = "yeizjigroup-secret";
    private String header = "token";
    private String prefix = "Bearer";
    private String roleClams = "roles";
    private long expired = 1000 * 60 * 60 * 24 * 7;

    private JwtUtilBuilder() {}

    public JwtUtilBuilder subject(String subject) {
      this.subject = subject;
      return this;
    }

    public JwtUtilBuilder secret(String secret) {
      this.secret = secret;
      return this;
    }

    public JwtUtilBuilder header(String header) {
      this.header = header;
      return this;
    }

    public JwtUtilBuilder prefix(String prefix) {
      this.prefix = prefix;
      return this;
    }

    public JwtUtilBuilder roleClams(String roleClams) {
      this.roleClams = roleClams;
      return this;
    }

    public JwtUtilBuilder expired(long expired) {
      this.expired = expired;
      return this;
    }

    public JwtUtils build() {
      JwtUtils jwtUtils = new JwtUtils();
      jwtUtils.subject = this.subject;
      jwtUtils.secret = this.secret;
      jwtUtils.header = this.header;
      jwtUtils.prefix = this.prefix;
      jwtUtils.roleClams = this.roleClams;
      jwtUtils.expired = this.expired;
      return jwtUtils;
    }
  }

  public static JwtUtilBuilder settings() {
    return new JwtUtilBuilder();
  }

  /**
   * 根据 map 生成 claims token
   *
   * @param map map 指定参数
   * @return token
   */
  public String createTokenByMap(Map<String, Object> map) {
    return Jwts.builder()
        .setClaims(map)
        .setIssuedAt(new Date())
        .setExpiration(new Date(System.currentTimeMillis() + expired))
        .signWith(SignatureAlgorithm.HS256, secret)
        .compact();
  }

  /**
   * 创建无过期token
   * @param map 指定参数
   * @return token
   */
  public String createTokenByMapNotExp(Map<String, Object> map) {
    return Jwts.builder()
            .setClaims(map)
            .setIssuedAt(new Date())
            .signWith(SignatureAlgorithm.HS256, secret)
            .setExpiration(new Date(System.currentTimeMillis() + expired*50))
            .compact();
  }

  /**
   * 根据 username 和 id 生成 token
   *
   * @param authorities 角色权限
   * @param id id
   * @param username username
   * @return token
   */
  public String createTokenWithIdAndUserName(Object authorities, Object id, Object username) {
    return Jwts.builder()
        .setSubject(subject)
        .claim(roleClams, authorities)
        .claim("id", id)
        .claim("username", username)
        .setIssuedAt(new Date())
        .setExpiration(new Date(System.currentTimeMillis() + expired))
        .signWith(SignatureAlgorithm.HS256, secret)
        .compact();
  }

  /**
   * 生成token
   *
   * @param username 用户名
   * @param role 角色定位
   * @return token
   */
  public String createToken(String username, String role) {
    Map<String, Object> map = new HashMap<>(1);
    map.put(roleClams, role);
    return Jwts.builder()
        .setSubject(username)
        .setClaims(map)
        .claim("username", username)
        .setIssuedAt(new Date())
        .setExpiration(new Date(System.currentTimeMillis() + expired))
        .signWith(SignatureAlgorithm.HS256, secret)
        .compact();
  }

  /**
   * 检测 jwt
   *
   * @param token token信息
   * @return 声明信息
   */
  public Claims checkJwt(String token) {
    try {
      return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
    } catch (Exception e) {
      e.printStackTrace();
      return null;
    }
  }

  /**
   * 获取用户名
   *
   * @param token token 信息
   * @return 用户名【插入 claims 之中】
   */
  public  String getUsername(String token) {
    Claims claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
    return claims.get("username").toString();
  }

  /**
   * 获取用户角色
   *
   * @param token token 信息
   * @return 用户角色定位
   */
  @SuppressWarnings("all")
  public List<SimpleGrantedAuthority> getUserRole(String token) {
    Claims claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
    List roles = (List) claims.get(roleClams);
    String json = JSONArray.toJSONString(roles);
    List<SimpleGrantedAuthority> grantedAuthorityList =
        JSONArray.parseArray(json, SimpleGrantedAuthority.class);
    return grantedAuthorityList;
  }

  /**
   * 是否过期
   *
   * @param token token信息
   * @return 判断 expire 是否过期
   */
  public boolean isExpiration(String token) {
    Claims claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
    return claims.getExpiration().before(new Date());
  }
}
